/*
 * serv_parese.c
 *
 *  Created on: 2012. 4. 13.
 *      Author: jphong
 */

#include	<ctype.h>
#include	"unp.h"
#include	"serv_parse.h"
#include	"chat.h"
#include	"clients.h"
#include	"debug.h"

// Parsing state. Response message format are declared
#define	S_OK			0
#define	S_USERLIST		1
#define	S_CLOSING		2
#define	S_LOGGEDIN		3
#define	S_TO			4
#define S_SYNTAX_ERR	5
#define S_NOTLOGGED		6
/////
#define S_FILEATTEMPT   7
#define S_ACCEPT		8
#define S_REJECT		9
////
#define	S_TO_ALL		10	// format not provided. Just a state

// Response message format
static char *responseFmt[] = {
		"200 OK\r\n\r\n",
		"210 users list\r\n%s\r\n\r\n",
		"221 Service closing\r\n\r\n",
		"230 User logged in\r\n\r\n",
		"299 Message delivered\r\n%s",
// Note: FROM method already includes <CRLF><CRLF>
		"500 Syntax error, command unrecognized\r\n\r\n",
		"530 %s: Not logged in\r\n\r\n",
		//
		"298 File transfer attempt\r\n%s",
		"227 %s %d Ready to receive file\r\n%s",
		"425 Rejected\r\n%s",
////////////////////////////////////

		};

/*
 *  Classify commands into token, using cmd[] definition
 */
static int cmdType(Cmd cmd[], char *word) {
	int i;

	for (i = 0; cmd[i].token; i++) {
		if (!strcmp(word, cmd[i].str))
			return (cmd[i].token);
	}
	return (UNKNOWN);
}

/*
 * Parse a request method, and produce the corresponding response message
 * 		FROM method is forwarded to the receivers
 * 	Returns:
 * 		1 if successful parsing and keep going on
 * 		0 if QUIT method is received
 */
int serv_parse(Client *client, const char *request, char *response) {
	char userList[MAXLINE];
	char forward[MAXLINE];
	char requestMSG[MAXID], filename[MAXID], FROMstr[MAXID], from[MAXID], TOstr[MAXID], to[MAXID];
	int token, nobody, state;
	int myfd, peerfd;
	Client *cl;
	int n;
	char sbuf[MAXLINE];

	*response = '\0';
	if ((n = sscanf(request, "%s %s %s %s %s %s", requestMSG, filename, FROMstr, from, TOstr, to)) <= 0)
		return (1); // No string there. Ignore
	token = cmdType(method, requestMSG);

	// Classify token into action, and check syntax
	switch (token) {
	case T_WHO: // "WHO\r\n\r\n",
		if (n == 1)
			state = S_USERLIST;
		else
			state = S_SYNTAX_ERR;
		break;
	case T_QUIT: // QUIT\r\n\r\n",

		if (n == 1)
			state = S_CLOSING;
		else
			state = S_SYNTAX_ERR;
		break;
	case T_USER: // "USER %s\r\n\r\n",
		bzero(requestMSG, MAXID);
		bzero(filename, MAXID);
		bzero(FROMstr, MAXID);
		bzero(from, MAXID);
		n = sscanf(request, "%s %s %s %s", FROMstr, from, TOstr, to);

		if (n == 2) {
			strncpy(client->user, from, MAXID);
			state = S_LOGGEDIN;
		} else
			state = S_SYNTAX_ERR;
		break;
	case T_FROM: // "FROM %s TO %s\r\n%s\r\n\r\n",
		bzero(requestMSG, MAXID);
		bzero(filename, MAXID);
		bzero(FROMstr, MAXID);
		bzero(from, MAXID);
		n = sscanf(request, "%s %s %s %s", FROMstr, from, TOstr, to);

		if (n >= 4 && !strcmp(TOstr, "TO")) {
			if (!strcmp(to, "all"))
				state = S_TO_ALL;
			else
				state = S_TO;
		} else
			state = S_SYNTAX_ERR;
		break;
	case T_SEND: // "SEND %s FROM %s TO %s\r\n\r\n",
		//
		if (n == 6 && !strcmp(TOstr, "TO")) {
			if (!strcmp(to, "all"))
				state = S_TO_ALL;
			else
				state = S_FILEATTEMPT;//S_TO;
		} else
			state = S_SYNTAX_ERR;
		break;
	case T_ACCEPT: // "ACCEPT %s FROM %s TO %s\r\n\r\n",
		state = S_ACCEPT;//
		break;
	case T_REJECT: // "REJECT %s FROM %s TO %s\r\n\r\n",
		state = S_REJECT;////
		break;
	case UNKNOWN:
		state = S_SYNTAX_ERR;
		break;
	default:
		state = S_SYNTAX_ERR;
		break;
	}

	// Generate response message depending on state
	switch (state) {
	case S_LOGGEDIN:
	case S_OK:
	case S_NOTLOGGED:
	case S_CLOSING:
	case S_SYNTAX_ERR:
		strcpy(response, responseFmt[state]);
		break;
	case S_USERLIST:
		*userList = '\0';
		for (cl = getClientList(); cl != NULL; cl = cl->next) {
			strcat(userList, "\t");
			if (getUser(cl) != NULL)
				strcat(userList, getUser(cl));
		}
		snprintf(response, MAXLINE, responseFmt[state], userList);
		break;

	case S_TO_ALL:
		if ((myfd = getSocket(findClient(from))) == -1) { // Sender not logged in
			snprintf(response, MAXLINE, responseFmt[S_NOTLOGGED], from);
			break;
		}
		nobody = 1;
		for (cl = getClientList(); cl != NULL; cl = cl->next) { // For each client
			if ((getUser(cl) != NULL) && ((peerfd = getSocket(cl)) != myfd)) { // logged user except sender
				snprintf(forward, MAXLINE, responseFmt[S_TO], request);
				writen(peerfd, forward, strlen(forward)); // forward to all user except for sender
				debug("forwarded to %s[%d] -->%s\n", cl->user, peerfd, forward);
				nobody = 0;
			}
		}
		if (nobody) // Nobody logged in execept sender
			snprintf(response, MAXLINE, responseFmt[S_NOTLOGGED], "nobody");
		else
			strcpy(response, responseFmt[S_OK]); // ACK
		break;
	case S_TO:
		if (getSocket(findClient(from)) == -1)
			snprintf(response, MAXLINE, responseFmt[S_NOTLOGGED], from); // Sender not logged in
		else if ((peerfd = getSocket(findClient(to))) == -1)
			snprintf(response, MAXLINE, responseFmt[S_NOTLOGGED], to); // Peer not logged in
		else {
			snprintf(forward, MAXLINE, responseFmt[S_TO], request);
			writen(peerfd, forward, strlen(forward)); // forward to  sender
			debug("forwarded to %s[%d] -->%s\n", to, peerfd, forward);
			strcpy(response, responseFmt[S_OK]); // ACK
		}
		break;

	case S_FILEATTEMPT:
		if (getSocket(findClient(from)) == -1)
			snprintf(response, MAXLINE, responseFmt[S_NOTLOGGED], from); // Sender not logged in
		else if ((peerfd = getSocket(findClient(to))) == -1)
			snprintf(response, MAXLINE, responseFmt[S_NOTLOGGED], to); // Peer not logged in
		else {
			snprintf(forward, MAXLINE, responseFmt[S_FILEATTEMPT], request);
			writen(peerfd, forward, strlen(forward)); // forward to  sender
			debug("SEND file forwarded to %s[%d] -->%s\n", to, peerfd, forward);
			strcpy(response, responseFmt[S_OK]); // ACK
		}
		break;

	case S_ACCEPT:
		getSocket(findClient(from));
		peerfd = getSocket(findClient(to));
		snprintf(forward, MAXLINE, responseFmt[S_ACCEPT], client->ipaddr, client->port, request);
		writen(peerfd, forward, strlen(forward)); // forward to  sender
		debug("ACCEPT file forward \n");
		strcpy(response, responseFmt[S_OK]); // ACK
		break;
	case S_REJECT:
		getSocket(findClient(from));
		peerfd = getSocket(findClient(to));
		snprintf(forward, MAXLINE, responseFmt[S_REJECT], request);
		writen(peerfd, forward, strlen(forward)); // forward to  sender
		debug("REJECT file forward \n");
		strcpy(response, responseFmt[S_OK]); // ACK
		break;
	}

	if (state == S_CLOSING)
		return (0); // QUIT received
	else
		return (1); // ReadBuffer  empty or not found any complete message (would blocked!)
}
